package com.maozzi.common.util;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.maozzi.common.constant.ErrorCodeConstant;
import com.maozzi.common.exception.BizException;
import com.maozzi.common.model.PageResultDTO;
import com.maozzi.common.model.PageResultVO;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.lang.Nullable;
import org.springframework.util.ObjectUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * 复制工具类
 *
 * @author maozi
 */
public class CloneUtil {

    /**
     * 单个对象之间的属性复制，适用于VO、DTO、DO之间的相互转换
     *
     * @param source 源对象
     * @param target 目标对象
     * @param <T>    源对象 泛型
     * @param <E>    目标对象 泛型
     * @return 返回目标对象
     */
    public static <T, E> E to(T source, E target) {
        if (ObjectUtils.isEmpty(source) || ObjectUtils.isEmpty(target)) {
            throw new BizException(ErrorCodeConstant.CAST_ERROR, "空数据类型");
        }
        BeanCopier copier = BeanCopier.create(source.getClass(), target.getClass(), false);
        copier.copy(source, target, null);
        return target;
    }

    /**
     * 单个对象之间的属性复制，适用于VO、DTO、DO之间的相互转换
     *
     * @param source 源对象
     * @param clz    目标Class类型
     * @param <T>    源对象 泛型
     * @param <E>    目标对象 泛型
     * @return 返回目标对象
     */
    public static <T, E> E to(T source, Class<E> clz) {
        return toInner(source, clz);
    }

    /**
     * 集合中对象之间属性复制
     *
     * @param source 源对象
     * @param clz    目标Class类型
     * @param <T>    源对象 泛型
     * @param <E>    目标对象 泛型
     * @return 返回目标对象
     */
    public static <T, E> List<E> toList(List<T> source, Class<E> clz) {
        List<E> list = new ArrayList<>();
        if (ObjectUtils.isEmpty(source)) {
            return list;
        }
        BeanCopier copier = null;
        for (T t : source) {
            if (ObjectUtils.isEmpty(copier)) {
                copier = BeanCopier.create(t.getClass(), clz, false);
            }
            list.add(toInner(t, clz, copier));
        }
        return list;
    }

    /**
     * 分页结果复制为分页DTO对象
     *
     * @param page 分页 源对象
     * @param clz  目标Class类型
     * @param <T>  源对象 泛型
     * @param <E>  目标对象 泛型
     * @return 返回目标对象
     */
    public static <T, E> PageResultDTO<E> toPageDTO(Page<T> page, Class<E> clz) {
        if (ObjectUtils.isEmpty(page)) {
            throw new BizException(ErrorCodeConstant.CAST_ERROR, "空数据类型");
        }
        PageResultDTO<E> resultDTO = new PageResultDTO<>(page.getCurrent(), page.getSize());
        resultDTO.setPageNum(page.getPages());
        resultDTO.setTotal(page.getTotal());
        BeanCopier copier = BeanCopier.create(page.getRecords().getClass(), clz, false);
        List<E> items = toList(page.getRecords(), clz);
        resultDTO.setItems(items);
        return resultDTO;
    }

    /**
     * 分页DTO结果复制为分页VO对象
     *
     * @param pageDTO 分页 源对象
     * @param clz     目标Class类型
     * @param <T>     源对象 泛型
     * @param <E>     目标对象 泛型
     * @return 返回目标对象
     */
    public static <T, E> PageResultVO<E> toPageVO(PageResultDTO<T> pageDTO, Class<E> clz) {
        PageResultVO<E> pageVO = new PageResultVO<>(pageDTO.getPage(), pageDTO.getPageSize());
        pageVO.setPageNum(pageDTO.getPageNum());
        pageVO.setTotal(pageDTO.getTotal());
        BeanCopier copier = BeanCopier.create(pageDTO.getItems().getClass(), clz, false);
        pageVO.setItems(toList(pageDTO.getItems(), clz));
        return pageVO;
    }

    /**
     * 单个bean之间的复制（内部）
     *
     * @param source 源对象
     * @param clz    目标Class类型
     * @param <T>    源对象 泛型
     * @param <E>    目标对象 泛型
     * @return 返回目标对象
     */
    private static <T, E> E toInner(T source, Class<E> clz) {
        if (ObjectUtils.isEmpty(source)) {
            throw new BizException(ErrorCodeConstant.CAST_ERROR, "空数据类型");
        }
        BeanCopier copier = BeanCopier.create(source.getClass(), clz, false);
        return toInner(source, clz, copier);
    }

    /**
     * 单个bean之间的复制（内部）
     *
     * @param source 源对象
     * @param clz    目标Class类型
     * @param copier cglib copier实例
     * @param <T>    源对象 泛型
     * @param <E>    目标对象 泛型
     * @return 返回目标对象
     */
    private static <T, E> E toInner(T source, Class<E> clz, BeanCopier copier) {
        if (ObjectUtils.isEmpty(source)) {
            throw new BizException(ErrorCodeConstant.CAST_ERROR, "空数据类型");
        }
        E target;
        try {
            target = clz.newInstance();
            copier.copy(source, target, null);
        } catch (Exception e) {
            throw new BizException(ErrorCodeConstant.CAST_ERROR, "类型转换错误");
        }
        return target;
    }
}
